/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
#ifndef PLIOBASE_H
#define PLIOBASE_H

#include <stddef.h>
using namespace std;

/**
 * Interface to the hardware's I/O capabilities.
 * Abstrace base class for hardware I/O capabilities.
 */
class PLIOBase {
public:
	/**
	 * The destructor does nothing.
	 */
	inline virtual ~PLIOBase() {}
	/**
	 * Get the size of the I/O region in bytes
	 *
	 * @return The size.
	 */
	virtual size_t size() const = 0;
	/**
	 * Read a byte (8bit) from the I/O Port or the memory-mapped I/O region.
	 *
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 * @return the byte (8bit) that have been read.
	 */
	virtual uint8_t read8(size_t offset = 0) = 0;
	/**
	 * Read two byte (16bit) from the I/O Port or the memory-mapped I/O region.
	 *
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 * @return the two byte (16bit) that have been read.
	 */
	virtual uint16_t read16(size_t offset = 0) = 0;
	/**
	 * Read four byte (32bit) from the I/O Port or the memory-mapped I/O region.
	 *
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 * @return the four byte (32bit) that have been read.
	 */
	virtual uint32_t read32(size_t offset = 0) = 0;
	/**
	 * Read eight byte (64bit) from the I/O Port or the memory-mapped I/O region. The
	 * 32bit at the lower address are read first, then the 32bit at the higher address.
	 *
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 * @return the eight byte (64bit) that have been read.
	 */
	inline uint64_t read64LowFirst(size_t offset = 0);
	/**
	 * Read eight byte (64bit) from the I/O Port or the memory-mapped I/O region. The
	 * 32bit at the higher address are read first, then the 32bit at the lower address.
	 *
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 * @return the eight byte (64bit) that have been read.
	 */
	inline uint64_t read64HighFirst(size_t offset = 0);
	/**
	 * Write a byte (8bit) to the I/O port or the memory-mapped I/O region.
	 *
	 * @param value the value that should be written.
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 */
	virtual void write8(uint8_t value, size_t offset = 0) = 0;
	/**
	 * Write two byte (16bit) to the I/O port or the memory-mapped I/O region.
	 *
	 * @param value the value that should be written.
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 */
	virtual void write16(uint16_t value, size_t offset = 0) = 0;
	/**
	 * Write four byte (32bit) to the I/O port or the memory-mapped I/O region.
	 *
	 * @param value the value that should be written.
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 */
	virtual void write32(uint32_t value, size_t offset = 0) = 0;
	/**
	 * Write eight byte (64bit) to the I/O Port or the memory-mapped I/O region. The
	 * 32bit at the lower address are written first, then the 32bit at the higher address.
	 *
	 * @param value the value that should be written.
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 */
	inline void write64LowFirst(uint64_t value, size_t offset = 0);
	/**
	 * Write eight byte (64bit) to the I/O Port or the memory-mapped I/O region. The
	 * 32bit at the higher address are written first, then the 32bit at the lower address.
	 *
	 * @param value the value that should be written.
	 * @param offset offset from the I/O base port or the I/O base memory address.
	 */
	inline void write64HighFirst(uint64_t value, size_t offset = 0);
	/**
	 * Check whether this class is usable.
	 *
	 * @return true, if the class holds a ressource that is correctly allocated and usable.
	 */
	virtual operator bool() const = 0;
protected:
	/**
	 * The default constructor does nothing.
	 */
	inline PLIOBase() {}
private:
	PLIOBase(const PLIOBase &);
	PLIOBase &operator = (const PLIOBase &);
};

uint64_t PLIOBase::read64LowFirst(size_t offset) {
	uint64_t low = read32(offset);
	uint64_t high = read32(offset + 4);
	return low | (high << 32);
}

uint64_t PLIOBase::read64HighFirst(size_t offset) {
	uint64_t high = read32(offset + 4);
	uint64_t low = read32(offset);
	return low | (high << 32);
}

void PLIOBase::write64LowFirst(uint64_t value, size_t offset) {
	write32(value & 0xFFFFFFFF, offset);
	write32(value >> 32, offset + 4);
}

void PLIOBase::write64HighFirst(uint64_t value, size_t offset) {
	write32(value >> 32, offset + 4);
	write32(value & 0xFFFFFFFF, offset);
}

#endif
